/*
============================================================================
 Name        : dd_local_audio_file.c
 Author      : Jian
 Version     :
 Copyright   : GPL
 Description : dd_local_audio_file.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "dd_local_audio_file.h"
#include "sd_platform.h"
#include "dd_mad.h"
#include "dd_player_config.h"
#include "dd_mp3_helper.h"
#include "dd_log.h"

typedef struct {
    dd_audio_file       funs ;
    
    int                 id ;

    DD_UINT             size ;
    sd_file*            file ;
    
    int                 isEof ;

    dd_audio_file_info* info ;

    DD_BYTE*            fileBuffer ;
    DD_UINT             bufLen ;
    
    dd_mad*             decoder ;
    mad_timer_t         timeCounter ;

    dd_mp3_helper*      mp3Reader ;

} dd_local_afile;


static int _id(dd_audio_file* pFile)
{
    dd_local_afile* file = (dd_local_afile*)pFile ;

    __DD_ASSERT_DEFENSE(file, return -1) ;

    return file->id ;
}

static int _info(dd_audio_file* pFile, const dd_audio_file_info** fileInfo) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;

    __DD_ASSERT_DEFENSE(file, return -1) ;

    if (!file->info) {
        int ret = dd_mp3h_read(file->mp3Reader, file->file, NULL) ;
        
        if (ret < 0) 
            return ret ;

        file->info = (dd_audio_file_info*)dd_malloc(sizeof(dd_audio_file_info)) ;
        __DD_CHECK_MEM(file->info, sizeof(dd_audio_file_info), return -1) ;
            
        strcpy(file->info->audioType, "mp3") ;
        file->info->id          = file->id ;
        file->info->fileSize    = dd_file_size(file->file, NULL) ;
        file->info->bitrate     = dd_mp3h_bitrate(file->mp3Reader) ;
        file->info->samplerate  = dd_mp3h_samplerate(file->mp3Reader) ;
        file->info->duration    = dd_mp3h_duration(file->mp3Reader) ;
        file->info->title       = dd_mp3h_title(file->mp3Reader) ;
        file->info->album       = dd_mp3h_album(file->mp3Reader) ;
        file->info->artist      = dd_mp3h_artist(file->mp3Reader) ;
        file->info->img         = dd_mp3h_image(file->mp3Reader) ;
    }
    
    *fileInfo = file->info ;
    return 0 ;
}

static int _decode(dd_audio_file* pFile, dd_pcm_buffer* pcmBuffer) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;
    int readed = 0 ;
    int decoded = 0 ;

    __DD_ASSERT_DEFENSE(file, return -1) ;
    
    readed = sd_file_read(file->file, file->fileBuffer + file->bufLen, ddPlayerFileBufSize - file->bufLen) ;

    if (readed < (ddPlayerFileBufSize - file->bufLen)) { 
        file->isEof = sd_file_eof(file->file) ;
        __DD_ASSERT_DEFENSE(file->isEof, dd_log_error("sd_file_read got error: %d", sd_file_error(file->file)); return -1) ;

        if (file->isEof && (file->bufLen + readed) == 0)
            return EEileEOF ;
    }

    file->bufLen += readed ;

    decoded = dd_mad_decode(file->decoder, file->fileBuffer, file->bufLen, pcmBuffer->data, &pcmBuffer->len) ;

    __DD_ASSERT_DEFENSE(decoded > 0, return (file->isEof) ? EEileEOF : -1) ;

    memmove(file->fileBuffer, file->fileBuffer + decoded, file->bufLen - decoded) ;
    file->bufLen -= decoded ;
    
    dd_mad_add_time(file->decoder, &file->timeCounter) ;

    return 0 ;
}

static int _tell(dd_audio_file* pFile) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;

    __DD_ASSERT_DEFENSE(file, return -1) ;

    return file->timeCounter.seconds ;   
}

static int _seek(dd_audio_file* pFile, DD_UINT second) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;
    int ret = -1 ;

    __DD_ASSERT_DEFENSE(file, return -1) ;
    
    if (dd_abs(file->timeCounter.seconds - second) <= 1) 
        return -1 ;

    ret = dd_mp3h_seek(file->mp3Reader, second) ;

    if (ret >= 0) {
        mad_timer_reset(&file->timeCounter);
        file->timeCounter.seconds = second ;
    }

    return ret ;
}

static int _is_eof(dd_audio_file* pFile) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;

    __DD_ASSERT_DEFENSE(file, return 0) ;

    return file->isEof ;
}

static void _destroy(dd_audio_file* pFile) 
{
    dd_local_afile* file = (dd_local_afile*)pFile ;

    __DD_ASSERT_DEFENSE(file, return) ;

    if (file->decoder)  
        dd_mad_destroy(file->decoder) ;
    
    if (file->fileBuffer)
        dd_free(file->fileBuffer) ;

    if (file->mp3Reader) 
        dd_mp3h_destroy(file->mp3Reader) ;

    if (file->file) 
        sd_file_close(file->file) ;

    if (file->info)
        dd_free(file->info) ;

    dd_free(file) ;
}

dd_audio_file* dd_lafile_create(int id) 
{
    dd_local_afile* file = (dd_local_afile*)dd_malloc(sizeof(dd_local_afile)) ;
    char fileName[DD_MAX_FILE_NAME_LEN + 1] = {0} ;

    __DD_CHECK_MEM(file, sizeof(dd_local_afile), return NULL) ;
    
    file->id            = id ;

    file->funs.id       = _id ;
    file->funs.info     = _info ;
    file->funs.decode   = _decode ;
    file->funs.is_eof   = _is_eof ;
    file->funs.tell     = _tell ;
    file->funs.seek     = _seek ;
    file->funs.destroy  = _destroy ;

    file->decoder = dd_mad_create() ;
    __DD_ASSERT_DEFENSE(file->decoder, return NULL) ;

    file->fileBuffer = (DD_BYTE*)dd_malloc(ddPlayerFileBufSize) ;
    __DD_CHECK_MEM(file->fileBuffer, ddPlayerFileBufSize, return NULL) ;

    file->mp3Reader = dd_mp3h_create() ;
     __DD_ASSERT_DEFENSE(file->mp3Reader, return NULL) ;
    
    if (sd_get_local_file_name(id, fileName) < 0) {
        dd_log_error("failed to get file name via id: %d", id) ;
        return NULL ;
    }
    
    if (sd_get_file_size((const DD_UTF8*)fileName, &file->size) < 0) {
        dd_log_error("failed to get file size, id: %d, name: %s", id, fileName) ;
        return NULL ;
    } 

    file->file = sd_file_open((const DD_UTF8*)fileName, DD_FILE_READ) ;
    __DD_ASSERT_DEFENSE(file->file, dd_log_error("failed to open file, id: %d, name: %s", id, fileName) ; return NULL) ;
    
    mad_timer_reset(&file->timeCounter);

    return &(file->funs) ;
}

/* End of File */
